////////////////////////////////////////////////////////////////////////////////

#include "client.h"

////////////////////////////////////////////////////////////////////////////////

#include "client_engine.h"
#include "client_network.h"
#include "client_gui.h"
#include "timer.h"

#include <boost/bind.hpp>
#include <iostream>

////////////////////////////////////////////////////////////////////////////////


Client::Client()
: m_io_service(NULL),
  m_Engine(NULL),
  m_Network(NULL),
  m_GUI(NULL)
{
    // Create io service object
    m_io_service = new boost::asio::io_service();

    // Create engine
    m_Engine = new ClientEngine(this);

    // Create network
    m_Network = new ClientNetwork("37015", this, *m_io_service );

    // Create gui
    m_GUI = new ClientGUI(this, *m_io_service);
}


////////////////////////////////////////////////////////////////////////////////


Client::~Client()
{
    // Cleanup
    if (m_Engine)
        delete m_Engine;
    if (m_GUI)
        delete m_GUI;
    if (m_Network)
        delete m_Network;
    if (m_io_service)
    {
        m_io_service->stop();
        delete m_io_service;
    }
}


////////////////////////////////////////////////////////////////////////////////


void Client::Start()
{
    try
    {
        // Start game
        m_io_service->run();
    }
    catch(...)
    {
        return;
    }
}


////////////////////////////////////////////////////////////////////////////////


void Client::ProcessMouseMotion( unsigned short _x, unsigned short _y )
{
    // Redirect to engine
    m_Engine->ProcessMouseMotion(_x, _y);
}


////////////////////////////////////////////////////////////////////////////////


void Client::ProcessEvent( STM::Event _event )
{
    // Redirect to engine
    m_Engine->ProcessEvent(_event);
}


////////////////////////////////////////////////////////////////////////////////


void Client::Receive( const ServerPacket& _packet )
{
    // Get current time
    clock_t currentTime = clock() + m_ServerTime - m_ClientTime;
    assert(currentTime > 0);

    // Delay in milliseconds
    size_t defaultDelay = 50;

    // Delay in clocks
    clock_t delay = CLOCKS_PER_SEC/1000 * defaultDelay;

    // When to draw packet
    clock_t displayTime = _packet.m_CreationTime + delay;

    // Make delay if necessary
    if (displayTime > currentTime)
    {
        DeadLineTimerPtr timer ( new boost::asio::deadline_timer(*m_io_service) );
        timer->expires_from_now(boost::posix_time::milliseconds( (displayTime - currentTime) * CLOCKS_PER_SEC/1000 ) );
        timer->async_wait(boost::bind( &Client::Draw,
                                              this,
                                              _packet,
                                              boost::asio::placeholders::error,
                                              timer ) );
    }
    else
        // Redirect to GUI
        m_GUI->Draw(_packet);
}


////////////////////////////////////////////////////////////////////////////////


void Client::Send( const ClientPacket& _packet )
{
    // Redirect to network
    m_Network->Send(_packet);
}


////////////////////////////////////////////////////////////////////////////////


void Client::SendEvents()
{
    ClientPacket packet;

    // Fetch client packet
    packet.m_ClientID = m_ClientID;
    packet.m_KeyFlags = m_Engine->GetKeyFlags();
    packet.m_MousePosition = m_Engine->GetMousePosition();

    // Do not send same packet twice
    static ClientPacket m_PrevPacket = packet;
    if (packet != m_PrevPacket)
    {
        m_PrevPacket = packet;
        Send(packet);
    }
}


////////////////////////////////////////////////////////////////////////////////


void Client::SetID( char _id )
{
    m_ClientID = _id;
}


////////////////////////////////////////////////////////////////////////////////


void Client::SyncronizeTime( clock_t _serverTime, clock_t _clientTime )
{
    m_ServerTime = _serverTime;
    m_ClientTime = _clientTime;
}


////////////////////////////////////////////////////////////////////////////////


void Client::Draw( const ServerPacket& _packet,
                   const boost::system::error_code& _error,
                   DeadLineTimerPtr _deadTimer )
{
    if (!_error)
        // Redirect to GUI
        m_GUI->Draw(_packet);
    else
        std::cout << _error.message() << std::endl;
}


////////////////////////////////////////////////////////////////////////////////
